{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/Aithu-Snehith/End-to-End-Learning-of-Communications-Systems-Without-a-Channel-Model/blob/master/4_pam_model.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "MS8D7jiknmCb"
   },
   "source": [
    "### Libraries required for the implementation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "YD6Q1OO06bA2"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "import matplotlib.pyplot as plt\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras.layers import *\n",
    "from sklearn import preprocessing\n",
    "import tensorflow.keras.backend as K\n",
    "from sklearn.metrics import mean_squared_error"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "R4dK64XLnmCk"
   },
   "source": [
    "### Required Parameters (4-PAM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Mamp_sR48wBd"
   },
   "outputs": [],
   "source": [
    "msg_total = 4\n",
    "channel = 8\n",
    "epochs = 5000\n",
    "sigma = 1e-4\n",
    "batch_size = 1024"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "n8CHc96cnmCp"
   },
   "source": [
    "### Defining Required Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "cUhpbsruIeoE"
   },
   "outputs": [],
   "source": [
    "def perturbation(x):\n",
    "    w = K.random_normal(shape = (channel,2),mean=0.0,stddev=sigma**0.5,dtype=None)\n",
    "    xp = ((1-sigma)**0.5)*x + w\n",
    "    return xp\n",
    "\n",
    "def loss_tx(y_true, y_pred):\n",
    "    return -y_true*y_pred\n",
    "\n",
    "def get_policy(inp):\n",
    "    xp = inp[0]\n",
    "    x = inp[1]\n",
    "    w = xp - x\n",
    "    policy = -K.sum(w*w)\n",
    "    return policy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Q7m4Li0RnmCt"
   },
   "source": [
    "## Modelling the Transmitter\n",
    "### 1. Tx encoder architecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 88
    },
    "colab_type": "code",
    "id": "62RZYX6p-Q91",
    "outputId": "af39be90-92c2-41e9-8d89-a3beba421eb6"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Colocations handled automatically by placer.\n"
     ]
    }
   ],
   "source": [
    "tx_inp = Input((1,))\n",
    "embbedings_layer = Dense(msg_total, activation = 'relu')(tx_inp)\n",
    "layer_dense = Dense(2*channel, activation = 'relu')(embbedings_layer)\n",
    "to_complex = Reshape((channel,2))(layer_dense)\n",
    "x = Lambda(lambda x: keras.backend.l2_normalize(x))(to_complex)\n",
    "xp = Lambda(perturbation)(to_complex)\n",
    "policy = Lambda(get_policy)([xp,x])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "J9wlYrnfnmC0"
   },
   "source": [
    "### 2. Definng Models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 408
    },
    "colab_type": "code",
    "id": "tdq7Oou4J1ZL",
    "outputId": "f430ff01-6992-4707-b9bc-287c416bed7e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__________________________________________________________________________________________________\n",
      "Layer (type)                    Output Shape         Param #     Connected to                     \n",
      "==================================================================================================\n",
      "input_1 (InputLayer)            (None, 1)            0                                            \n",
      "__________________________________________________________________________________________________\n",
      "dense (Dense)                   (None, 4)            8           input_1[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dense_1 (Dense)                 (None, 16)           80          dense[0][0]                      \n",
      "__________________________________________________________________________________________________\n",
      "reshape (Reshape)               (None, 8, 2)         0           dense_1[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda_1 (Lambda)               (None, 8, 2)         0           reshape[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda (Lambda)                 (None, 8, 2)         0           reshape[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda_2 (Lambda)               ()                   0           lambda_1[0][0]                   \n",
      "                                                                 lambda[0][0]                     \n",
      "==================================================================================================\n",
      "Total params: 88\n",
      "Trainable params: 88\n",
      "Non-trainable params: 0\n",
      "__________________________________________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "model_policy = keras.models.Model(inputs=tx_inp, outputs=policy)\n",
    "model_tx = keras.models.Model(inputs=tx_inp, outputs=xp)\n",
    "model_x = keras.models.Model(inputs=tx_inp, outputs=x)\n",
    "\n",
    "model_policy.compile(loss=loss_tx, optimizer=tf.keras.optimizers.SGD(lr = 1e-5))\n",
    "print(model_policy.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "YHlsMKNdnmC5"
   },
   "source": [
    "## Modelling the Receiver\n",
    "### Rx architecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 289
    },
    "colab_type": "code",
    "id": "jPJKGpLLLqbo",
    "outputId": "05309052-65dd-4ab1-8d1d-6b010cd231a4"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_2 (InputLayer)         (None, 8, 2)              0         \n",
      "_________________________________________________________________\n",
      "reshape_1 (Reshape)          (None, 16)                0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 128)               2176      \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 4)                 516       \n",
      "=================================================================\n",
      "Total params: 2,692\n",
      "Trainable params: 2,692\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "rx_inp = Input((channel,2))\n",
    "to_flat = Reshape((2*channel,))(rx_inp)\n",
    "fc = Dense(8*2*channel, activation = 'relu')(to_flat)\n",
    "softmax = Dense(msg_total, activation = 'softmax')(fc)\n",
    "\n",
    "model_rx = keras.models.Model(inputs=rx_inp, outputs=softmax)\n",
    "\n",
    "model_rx.compile(loss=tf.keras.losses.categorical_crossentropy, optimizer=tf.keras.optimizers.Adam())\n",
    "print(model_rx.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "9JxRBu5JnmC_"
   },
   "source": [
    "### Alternate Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 938
    },
    "colab_type": "code",
    "id": "g6h-Vx6JpksG",
    "outputId": "201d04ef-bc16-4592-8c25-751c399ba4cb"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use tf.cast instead.\n",
      "epoch:  0 tx_loss 114.35173034667969 rx_loss 1.3796947002410889\n",
      "epoch:  100 tx_loss 6.021037578582764 rx_loss 1.0343232154846191\n",
      "epoch:  200 tx_loss 3.665850877761841 rx_loss 0.8181131482124329\n",
      "epoch:  300 tx_loss 1.520128846168518 rx_loss 0.7042916417121887\n",
      "epoch:  400 tx_loss 1.4927887916564941 rx_loss 0.5554989576339722\n",
      "epoch:  500 tx_loss 1.473479986190796 rx_loss 0.4429166615009308\n",
      "epoch:  600 tx_loss 0.7329232692718506 rx_loss 0.375607967376709\n",
      "epoch:  700 tx_loss 1.388350248336792 rx_loss 0.33823221921920776\n",
      "epoch:  800 tx_loss 1.3279764652252197 rx_loss 0.3101707100868225\n",
      "epoch:  900 tx_loss 1.0778858661651611 rx_loss 0.2860800325870514\n",
      "epoch:  1000 tx_loss 1.438643455505371 rx_loss 0.26754045486450195\n",
      "epoch:  1100 tx_loss 2.3404417037963867 rx_loss 0.23381781578063965\n",
      "epoch:  1200 tx_loss 1.3312132358551025 rx_loss 0.21527284383773804\n",
      "epoch:  1300 tx_loss 1.4248260259628296 rx_loss 0.19488610327243805\n",
      "epoch:  1400 tx_loss 1.0382447242736816 rx_loss 0.16341015696525574\n",
      "epoch:  1500 tx_loss 1.3557523488998413 rx_loss 0.1455385982990265\n",
      "epoch:  1600 tx_loss 2.2829983234405518 rx_loss 0.12694334983825684\n",
      "epoch:  1700 tx_loss 2.4766950607299805 rx_loss 0.10819017142057419\n",
      "epoch:  1800 tx_loss 1.146506905555725 rx_loss 0.0801992416381836\n",
      "epoch:  1900 tx_loss 2.664935827255249 rx_loss 0.07315555214881897\n",
      "epoch:  2000 tx_loss 1.6458134651184082 rx_loss 0.05360748618841171\n",
      "epoch:  2100 tx_loss 2.188070774078369 rx_loss 0.05371185764670372\n",
      "epoch:  2200 tx_loss 1.9517011642456055 rx_loss 0.04446491599082947\n",
      "epoch:  2300 tx_loss 4.250457763671875 rx_loss 0.04665788263082504\n",
      "epoch:  2400 tx_loss 2.6695938110351562 rx_loss 0.04169715940952301\n",
      "epoch:  2500 tx_loss 1.7643426656723022 rx_loss 0.035005323588848114\n",
      "epoch:  2600 tx_loss 2.1374576091766357 rx_loss 0.03542027622461319\n",
      "epoch:  2700 tx_loss 1.144116759300232 rx_loss 0.03213422745466232\n",
      "epoch:  2800 tx_loss 3.3196122646331787 rx_loss 0.027622252702713013\n",
      "epoch:  2900 tx_loss 3.393239736557007 rx_loss 0.02473902888596058\n",
      "epoch:  3000 tx_loss 1.9258756637573242 rx_loss 0.01998080685734749\n",
      "epoch:  3100 tx_loss 1.7442213296890259 rx_loss 0.022557873278856277\n",
      "epoch:  3200 tx_loss 0.9708209037780762 rx_loss 0.02007543109357357\n",
      "epoch:  3300 tx_loss 2.605186700820923 rx_loss 0.01592843048274517\n",
      "epoch:  3400 tx_loss 3.0331175327301025 rx_loss 0.017260106280446053\n",
      "epoch:  3500 tx_loss 2.038749933242798 rx_loss 0.013623886741697788\n",
      "epoch:  3600 tx_loss 3.589238405227661 rx_loss 0.013174154795706272\n",
      "epoch:  3700 tx_loss 3.232999801635742 rx_loss 0.014157405123114586\n",
      "epoch:  3800 tx_loss 3.5114808082580566 rx_loss 0.012510138563811779\n",
      "epoch:  3900 tx_loss 2.2777552604675293 rx_loss 0.01225421205163002\n",
      "epoch:  4000 tx_loss 1.2391116619110107 rx_loss 0.00964068528264761\n",
      "epoch:  4100 tx_loss 3.3996007442474365 rx_loss 0.00860733911395073\n",
      "epoch:  4200 tx_loss 3.4578404426574707 rx_loss 0.007440897636115551\n",
      "epoch:  4300 tx_loss 2.264455556869507 rx_loss 0.006812898442149162\n",
      "epoch:  4400 tx_loss 1.8350210189819336 rx_loss 0.00730531383305788\n",
      "epoch:  4500 tx_loss 1.1498150825500488 rx_loss 0.008417153730988503\n",
      "epoch:  4600 tx_loss 3.6709954738616943 rx_loss 0.006787853315472603\n",
      "epoch:  4700 tx_loss 1.7060564756393433 rx_loss 0.006307173985987902\n",
      "epoch:  4800 tx_loss 1.1874375343322754 rx_loss 0.004096090793609619\n",
      "epoch:  4900 tx_loss 3.6314749717712402 rx_loss 0.004732245579361916\n"
     ]
    }
   ],
   "source": [
    "loss_tx = []\n",
    "loss_rx = []\n",
    "for epoch in range(epochs):\n",
    "#     Transmitter Training\n",
    "    raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "    label = np.zeros((batch_size, msg_total))\n",
    "    label[np.arange(batch_size), raw_input] = 1\n",
    "    tx_input = raw_input/float(msg_total)\n",
    "    xp = model_tx.predict(tx_input)\n",
    "    y = xp + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "    pred = model_rx.predict(y)\n",
    "    loss = np.sum(np.square(label - pred), axis = 1)\n",
    "    history_tx = model_policy.fit(tx_input, loss, batch_size=batch_size, epochs=1, verbose=0)    \n",
    "    loss_tx.append(history_tx.history['loss'][0])\n",
    "    \n",
    "#     Receiver Training\n",
    "    raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "    label = np.zeros((batch_size, msg_total))\n",
    "    label[np.arange(batch_size), raw_input] = 1\n",
    "    tx_input = raw_input/float(msg_total)\n",
    "    x = model_x.predict(tx_input)\n",
    "    y = x + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "    history_rx = model_rx.fit(y, label, batch_size=batch_size, epochs=1, verbose=0)\n",
    "    loss_rx.append(history_rx.history['loss'][0])\n",
    "    \n",
    "    if(epoch % 100 == 0):\n",
    "        print('epoch: ', epoch, 'tx_loss', history_tx.history['loss'][0], 'rx_loss', history_rx.history['loss'][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "cuAVy7XCnxb_"
   },
   "source": [
    "### Plotting Transmitter and Receiver Loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 350
    },
    "colab_type": "code",
    "id": "YOax3T0THnrF",
    "outputId": "8d59d150-962f-44f4-a56c-d22acd9a270d"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nLAKRFl8zAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 864x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "tags": []
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize = (12,5))\n",
    "plt.subplot(1,2,1)\n",
    "plt.plot(loss_tx)\n",
    "plt.title('Transmitter Loss')\n",
    "plt.xlabel('epochs')\n",
    "plt.ylabel('loss')\n",
    "plt.subplot(1,2,2)\n",
    "plt.plot(loss_rx)\n",
    "plt.title('Receiver Loss')\n",
    "plt.xlabel('epochs')\n",
    "plt.ylabel('loss')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "yVeQY774nmDK"
   },
   "source": [
    "### Prediction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 136
    },
    "colab_type": "code",
    "id": "Ob52RzJevSF2",
    "outputId": "ed984937-d9c4-4487-cc52-943b047f1948"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2 0 2 0 0 3 2 1 3 0 0 2 2 3 1 2 3 1 1 3 0 0 3 3 0 3 3 3 3 0 2 1 2 3 2 1 3\n",
      " 2 0 0 0 2 2 2 0 0 3 3 0 2 3 2 1 2 3 1 0 3 1 0 1 2 0 2 3 1 3 3 0 2 2 1 1 1\n",
      " 0 0 0 0 3 0 0 1 2 2 3 0 1 1 3 0 2 3 1 1 1 0 0 3 0 2]\n",
      "[2 0 2 0 0 3 2 1 3 0 0 2 2 3 1 2 3 1 1 3 0 0 3 3 0 3 3 3 3 0 2 1 2 3 2 1 3\n",
      " 2 0 0 0 2 2 2 0 0 3 3 0 2 3 2 1 2 3 1 0 3 1 0 1 2 0 2 3 1 3 3 0 2 2 1 1 1\n",
      " 0 0 0 0 3 0 0 1 2 2 3 0 1 1 3 0 2 3 1 1 1 0 0 3 0 2]\n",
      "accuracy: 1.0\n"
     ]
    }
   ],
   "source": [
    "#testing\n",
    "batch_size = 100\n",
    "raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "print(raw_input)\n",
    "label = np.zeros((batch_size, msg_total))\n",
    "label[np.arange(batch_size), raw_input] = 1\n",
    "tx_input = raw_input/float(msg_total)\n",
    "xp = model_x.predict(tx_input)\n",
    "y = xp + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "pred = model_rx.predict(y)\n",
    "pred_int = np.argmax(pred, axis = 1)\n",
    "print(pred_int)\n",
    "\n",
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "print('accuracy:',accuracy_score(raw_input, pred_int))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "v0SWR8noCoc6"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "include_colab_link": true,
   "name": "4_pam_model.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
